#include <jni.h>
#include <stdio.h>
#include <android/log.h>
#include <sys/stat.h>
#include "include/ffmpeg/libavcodec/avcodec.h"
#include "include/ffmpeg/libavformat/avformat.h"
#include "include/ffmpeg/libswscale/swscale.h"
#include "include/ffmpeg/libswresample/swresample.h"

#define LOGE(FORMAT,...) __android_log_print(6, "Ffmpeg", FORMAT, __VA_ARGS__);
#define MAX_AUDIO_FRME_SIZE 48000 * 4
/*
 * Class:     com_ndk_ffmpeg_FfmpegUtil
 * Method:    video_decode
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */

JNIEXPORT void JNICALL Java_com_ndk_ffmpeg_FfmpegUtil_videoDecode
  (JNIEnv *env, jclass cls, jstring input, jstring output){
	const char *cstr_input = (*env)->GetStringUTFChars(env, input, JNI_FALSE);
	const char *cstr_output = (*env)->GetStringUTFChars(env, output, JNI_FALSE);
	LOGE("input = %s", cstr_input);
	LOGE("output = %s", cstr_output);

	FILE *fp = fopen(cstr_output, "wb");

	av_register_all();

	AVFormatContext *pFormatCtx = avformat_alloc_context();

	int avformat_open_input_result = avformat_open_input(&pFormatCtx, cstr_input, NULL, NULL);
	LOGE("avformat_open_input_result = %d", avformat_open_input_result);

	int avformat_find_stream_info_result = avformat_find_stream_info(pFormatCtx, NULL);
	LOGE("avformat_find_stream_info_result = %d", avformat_find_stream_info_result);

	LOGE("pFormatCtx->bit_rate = %d", pFormatCtx->bit_rate);
	LOGE("pFormatCtx->duration = %d", pFormatCtx->duration);

	int i=0;
	int video_stream_index = -1;
	for(; i<pFormatCtx->nb_streams; i++){
		if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
			video_stream_index = i;
			break;
		}
	}
	LOGE("video_stream_index = %d", video_stream_index);

	AVCodecContext *pCodecCtx = pFormatCtx->streams[video_stream_index]->codec;
	AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if(pCodec==NULL){
		LOGE("%s", "get decoder fail");
		return;
	}else{
		LOGE("%s", "get decoder success");
	}

	int avcodec_open2_result = avcodec_open2(pCodecCtx, pCodec, NULL);
	LOGE("avcodec_open2_result = %d", avcodec_open2_result);

	AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
	av_init_packet(packet);
	AVFrame *frame = av_frame_alloc();
	int got_picture_ptr=0;
	int size = pCodecCtx->width*pCodecCtx->height;
	LOGE("pCodecCtx->width = %d", pCodecCtx->width);
	LOGE("pCodecCtx->height = %d", pCodecCtx->height);


	AVFrame *yuv_frame = av_frame_alloc();
	uint8_t *out_buff = av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
	avpicture_fill((AVPicture*)yuv_frame, out_buff, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

	struct SwsContext *swsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
			pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
	        SWS_BILINEAR, NULL,NULL, NULL);

	while(av_read_frame(pFormatCtx, packet)>=0){
		if (packet->stream_index == video_stream_index) {
			int avcodec_decode_video2_result = avcodec_decode_video2(pCodecCtx, frame, &got_picture_ptr, packet);
			LOGE("avcodec_decode_video2_result = %d", avcodec_decode_video2_result);
			if(avcodec_decode_video2_result>=0){

				sws_scale(swsCtx, frame->data,
			              frame->linesize, 0, frame->height,
			              yuv_frame->data, yuv_frame->linesize);

				fwrite(yuv_frame->data[0], 1, size, fp);
				fwrite(yuv_frame->data[1], 1, size/4, fp);
				fwrite(yuv_frame->data[2], 1, size/4, fp);
			}
		}
		av_free_packet(packet);
	}

	LOGE("%s", "decode complete");

	av_frame_free(&yuv_frame);
	av_frame_free(&frame);
	sws_freeContext(swsCtx);
	avcodec_close(pCodecCtx);
	avformat_free_context(pFormatCtx);
	fclose(fp);
	(*env)->ReleaseStringUTFChars(env, input, cstr_input);
	(*env)->ReleaseStringUTFChars(env, output, cstr_output);
}

/*
 * Class:     com_ndk_ffmpeg_FfmpegUtil
 * Method:    video_encode
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ndk_ffmpeg_FfmpegUtil_videoEncode
 (JNIEnv *env, jclass cls, jstring input, jstring output){

}

/*
 * Class:     com_ndk_ffmpeg_FfmpegUtil
 * Method:    audio_decode
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ndk_ffmpeg_FfmpegUtil_audioDecode
 (JNIEnv *env, jclass cls, jstring input, jstring output){

	char *cstr_input = (*env)->GetStringUTFChars(env, input, NULL);
	char *cstr_output = (*env)->GetStringUTFChars(env, output, NULL);

	FILE *fp = fopen(cstr_output, "wb");

	av_register_all();
	AVFormatContext *pFormatCtx = avformat_alloc_context();

	int avformat_open_input_result = avformat_open_input(&pFormatCtx, cstr_input, NULL, NULL);
	LOGE("avformat_open_input_result = %d", avformat_open_input_result);

	int avformat_find_stream_info_result = avformat_find_stream_info(pFormatCtx, NULL);
	LOGE("avformat_find_stream_info_result = %d", avformat_find_stream_info_result);

	int i=0;
	int audio_stream_index = -1;
	for(; i<pFormatCtx->nb_streams; i++){
		if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
			audio_stream_index = i;
			break;
		}
	}
	LOGE("audio_stream_index = %d", audio_stream_index);

	AVCodecContext *pCodecCtx = pFormatCtx->streams[audio_stream_index]->codec;
	AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	int avcodec_open2_result = avcodec_open2(pCodecCtx, pCodec, NULL);
	LOGE("avcodec_open2_result = %d", avcodec_open2_result);

	AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
	AVFrame *frame = av_frame_alloc();
	int got_frame_ptr = 0;

	struct SwrContext *swrCtx = swr_alloc();
	int64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
	enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;
	int64_t  in_ch_layout = pCodecCtx->channel_layout;
	enum AVSampleFormat in_sample_fmt = pCodecCtx->sample_fmt;
	int  in_sample_rate = pCodecCtx->sample_rate;
	swr_alloc_set_opts(swrCtx,
			out_ch_layout, out_sample_fmt, out_sample_rate,
			in_ch_layout, in_sample_fmt, in_sample_rate, 0, NULL);
	swr_init(swrCtx);

	int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
	uint8_t *out = (uint8_t*)av_malloc(MAX_AUDIO_FRME_SIZE);

	while(av_read_frame(pFormatCtx, packet)>=0){

		if(packet->stream_index == audio_stream_index){

			int avcodec_decode_audio4_result = avcodec_decode_audio4(pCodecCtx, frame, &got_frame_ptr, packet);
			if(avcodec_decode_audio4_result>=0){

				int swr_convert_result = swr_convert(swrCtx, &out, MAX_AUDIO_FRME_SIZE, frame->data, frame->nb_samples);
				int buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb, frame->nb_samples, out_sample_fmt, 1);
				fwrite(out, 1, buffer_size, fp);
			}
		}
		av_free_packet(packet);
	}

	LOGE("%s", "decode complete");

	fclose(fp);
	av_frame_free(&frame);
	swr_free(&swrCtx);
	av_free(out);
	avformat_close_input(&pFormatCtx);
	avcodec_close(pCodecCtx);

	(*env)->ReleaseStringUTFChars(env, input, cstr_input);
	(*env)->ReleaseStringUTFChars(env, output, cstr_output);
}

/*
 * Class:     com_ndk_ffmpeg_FfmpegUtil
 * Method:    audio_encode
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ndk_ffmpeg_FfmpegUtil_audioEncode
 (JNIEnv *env, jclass cls, jstring input, jstring output){

}
